#include "CH58x_common.h"
#include "zq_device.h"
#include "task_dbg.h"

//#define SIMU_GPIO_VALID_ASSERT(gpio) assert((gpio < 16) || (gpio >= 32 && gpio < (32+24)))
#define SIMU_GPIO_VALID_ASSERT(gpio)

extern uint64_t diag_gpio_val;
uint64_t simu_gpio_in = 0;
uint64_t simu_gpio_irq = 0;
uint64_t simu_gpio_it1 = 0;
uint64_t simu_gpio_it2 = 0;
uint64_t simu_gpio_it_flag = 0;


void GPIO_ModeCfg(uint8_t gpio, GPIOModeTypeDef mode)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    if(mode == GPIO_ModeIN_Floating || mode == GPIO_ModeIN_PD || mode == GPIO_ModeIN_PU)
        simu_gpio_in |= (1ll << gpio);
    else
        simu_gpio_in &= ~(1ll << gpio);
}
void GPIO_ModeCfgAll(GPIOModeTypeDef mode)
{
    for (uint8_t i = 0; i < 16; i++)
        GPIO_ModeCfg(i, mode);
    for (uint8_t i = 32; i < 32+24; i++)
        GPIO_ModeCfg(i, mode);
}
void GPIO_ResetBits(uint8_t gpio)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    diag_gpio_val &= ~(1ll << gpio);
}
void GPIO_SetBits(uint8_t gpio)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    diag_gpio_val |= (1ll << gpio);
}
bool GPIO_ReadPortPin(uint8_t gpio)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    return diag_gpio_val & (1ll << gpio);
}
void GPIO_InverseBits(uint8_t gpio)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    if (GPIO_ReadPortPin(gpio)) GPIO_ResetBits(gpio);
    else GPIO_SetBits(gpio);
}
void GPIO_EnableIRQ(uint8_t gpio, bool en)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    simu_gpio_irq = en ? (simu_gpio_irq | (1ll << gpio)) : (simu_gpio_irq & ~(1ll << gpio));
}
// 配置GPIO中断模式, 注意: 配置完会启动中断, 相当于隐式调用GPIO_EnableIRQ
void GPIO_ITModeCfg(uint8_t gpio, GPIOITModeTpDef mode)
{
    SIMU_GPIO_VALID_ASSERT(gpio);
    switch (mode) {
        case GPIO_ITMode_FallEdge:
            simu_gpio_it1 &= ~(1ll << gpio);
            simu_gpio_it2 &= ~(1ll << gpio);
            break;
        case GPIO_ITMode_HighLevel:
            simu_gpio_it1 |= (1ll << gpio);
            simu_gpio_it2 &= ~(1ll << gpio);
            break;
        case GPIO_ITMode_LowLevel:
            simu_gpio_it1 &= ~(1ll << gpio);
            simu_gpio_it2 |= (1ll << gpio);
            break;
        case GPIO_ITMode_RiseEdge:
            simu_gpio_it1 |= (1ll << gpio);
            simu_gpio_it2 |= (1ll << gpio);
            break;
    }
    GPIO_EnableIRQ(gpio, true);
}
void GPIOA_ClearITFlagBit(uint16_t gpio_pin)
{
    simu_gpio_it_flag &= ~gpio_pin;
}
void GPIOB_ClearITFlagBit(uint16_t gpio_pin)
{
    simu_gpio_it_flag &= ~(gpio_pin << 32);
}
uint16_t GPIOA_ReadITFlagPort(void)
{
    return (uint16_t)(simu_gpio_it_flag & 0xffff);
}
uint16_t GPIOB_ReadITFlagPort(void)
{
    return (uint16_t)((simu_gpio_it_flag >> 32) & 0xffff);
}

void SimuGPIOInt(uint64_t val)
{
    for (int i = 0; i < 64; i++) {
        uint64_t mask = (1ll << i);
        if ((simu_gpio_in & mask) == 0) continue; // no input
        if ((simu_gpio_irq & mask) == 0) continue; // no irq
        uint64_t last_gpio = diag_gpio_val & mask;
        uint64_t new_gpio = val & mask;
        if (!new_gpio && last_gpio && !(simu_gpio_it1 & mask) && !(simu_gpio_it2 & mask)) 
            simu_gpio_it_flag |= mask; // fall edge int
        else if (new_gpio && (simu_gpio_it1 & mask) && !(simu_gpio_it2 & mask)) 
            simu_gpio_it_flag |= mask; // high level int
        else if (!new_gpio && !(simu_gpio_it1 & mask) && (simu_gpio_it2 & mask)) 
            simu_gpio_it_flag |= mask; // level level int
        else if (new_gpio && !last_gpio && (simu_gpio_it1 & mask) && (simu_gpio_it2 & mask)) 
            simu_gpio_it_flag |= mask; // raise edge int
    }
}
void SimuGPIOWake(void)
{
    extern int simu_sleep_ms;
    extern uint8_t simu_r8_wake_flag;

    if ((simu_r8_wake_flag | RB_SLP_GPIO_WAKE) && simu_gpio_it_flag != 0) 
        simu_sleep_ms = 0; // has interrupt, simu awake
}
void SimuGPIOInput(uint64_t val)
{
    // 保护一下，绝对不接收模拟器修改以下GPIO值，不管配位输入还是输出
    uint64_t safe_gpio_mask = (1ll << zqm_io_tb.cat1_en) | (1ll << zqm_io_tb.power_led);
    val = (val & ~safe_gpio_mask) | (diag_gpio_val & safe_gpio_mask);
    if ((diag_gpio_val & simu_gpio_in) == (val & simu_gpio_in)) return; // input no change

    SimuGPIOInt(val);
    SimuGPIOWake();

    diag_gpio_val = (diag_gpio_val & ~simu_gpio_in) | (val & simu_gpio_in);
}